# -*- coding: UTF-8 -*-
import json
import os
import sys
import xml.etree.ElementTree as ET


# """json
# {
#     "name": "",
#     "desc": "",
#     "objects": [
#         { "name": {
#             "objc": "adder",
#             "java": "com.baidu.adder",
#             "python": "add"
#         },
#         "versions": [ 12,14 ],
#         "funcs":[
#             {   "name": {"objc": "add:with", "java": "add", "python": "add"},
#                 "args":[
#                     {"type": "number", "flags": [ "required" ]},
#                     {"type": "number", "flags": [ "required" ]}
#                 ]
#             },
#
#             {   "name": {"objc": "sub:with", "java": "sub", "python": "sub"},
#                 "args": [
#                     {"type": "number", "flags": [ "required" ]},
#                     {"type": "number", "flags": [ "required" ]}
#             }
#         ]
#         }
#     ]
# }
# """


class Name:
    def __init__(self):
        self.objc = ''
        self.python = ''
        self.java = ''

    @classmethod
    def load(cls, node: ET.Element) -> ():
        name = Name()
        name.objc = node.get('objc')
        if name.objc is None:
            return None, '\'objc\' is missing'

        name.java = node.get('java')
        if name.java is None:
            return None, '\'java\' is missing'

        name.python = node.get('python')
        if name.python is None:
            return None, '\'python\' is missing'

        return name, ''

    def encode(self) -> {}:
        return {
            "objc": self.objc,
            "python": self.python,
            "java": self.java,
        }


class Arg:
    def __init__(self):
        self.type = ''
        self.optional = 0

    @classmethod
    def load(cls, arg_node: ET.Element) -> ():
        if arg_node.tag != 'arg':
            return None, 'Unsupported node {}'.format(arg_node.tag)

        arg = Arg()

        arg.type = arg_node.get('type')
        if arg.type is None:
            return None, '\'type\' is missing'

        arg.optional = arg_node.get('optional')

        return arg, ''

    def encode(self) -> {}:
        return {
            "type": self.type,
            "optional": self.optional,
        }


class Func:
    def __init__(self):
        self.name = None
        self.args = []

    @classmethod
    def load(cls, func_node: ET.Element) -> ():
        if func_node.tag != 'func':
            return None, 'Unsupported node {}'.format(func_node.tag)

        func = Func()

        func.name, error = Name.load(func_node)
        if func.name is None:
            return None, error

        for arg_node in func_node:
            arg, error = Arg.load(arg_node)
            if arg is None:
                return None, error
            func.args.append(arg)

        return func, ''

    def encode(self) -> {}:
        return {
            "name": self.name.encode(),
            "args": [arg.encode() for arg in self.args],
        }


class Object:
    def __init__(self):
        self.name = None
        self.versions = []
        self.funcs = []

    @classmethod
    def load(cls, object_node: ET.Element) -> ():
        if object_node.tag != 'object':
            return None, 'Unsupported node {}'.format(object_node.tag)

        obj = Object()

        #   读取名字属性
        obj.name, error = Name.load(object_node)
        if obj.name is None:
            return None, error

        #   读取函数列表
        obj.funcs = []
        for func_node in object_node:
            func, error = Func.load(func_node)
            if func is None:
                return None, error
            obj.funcs.append(func)

        return obj, ''

    def encode(self) -> {}:
        return {
            "name": self.name.encode(),
            "versions": self.versions,
            "funcs": [func.encode() for func in self.funcs],
        }


class Service:
    def __init__(self):
        self.objects = []

    @classmethod
    def load(cls, service_node: ET.Element) -> ():
        if service_node.tag != 'service':
            return None, 'Unsupported node {}'.format(service_node.tag)

        svc = Service()

        for object_node in service_node:
            obj, error = Object.load(object_node)
            if obj is None:
                return None, error
            svc.objects.append(obj)

        return svc, ''

    def encode(self) -> {}:
        return {
            "objects": [obj.encode() for obj in self.objects],
        }


# class SchemaEncoder(json.JSONEncoder):
#     def default(self, o: Any) -> Any:
#         if isinstance(o, Service):
#             return s.encode(self)


def load_schema_xml(filename: str) -> (Service, str):
    tree = ET.parse(filename)
    service_node = tree.getroot()
    return Service.load(service_node)


def gen_schema_json(obj: Service) -> (str, str):
    svc = obj.encode()
    try:
        s = json.dumps(svc, sort_keys=True, indent=4)
    except TypeError as e:
        return "", str(e)
    return s, ""


def cmd_compile(argv: [str]) -> int:
    param_file = ""
    output_dir = ""
    list_file = ""

    i = 1
    while i < len(argv):
        if argv[i] == '-l':
            if (i + 1) >= len(argv):
                print("Uncompleted option '-l'", file=sys.stderr)
                return 1
            list_file = argv[i + 1]
            i += 2
            continue
        if argv[i] == '-o':
            if (i + 1) >= len(argv):
                print("Uncompleted option '-o'", file=sys.stderr)
                return 1
            output_dir = argv[i + 1]
            i += 2
            continue

        if param_file != "":
            print("Unsupported option '{}'".format(argv[i]), file=sys.stderr)
            return 1
        param_file = argv[i]
        i += 1
        continue

    #   输入参数文件需要存在
    if not os.path.isfile(param_file):
        print("The parameter file is not exist or accessable: '{}'".format(param_file), file=sys.stderr)

    #   如果输出目录未指定，那么指定为当前工作目录
    if output_dir == "":
        output_dir = os.getcwd()

    if not os.path.isdir(output_dir):
        os.makedirs(output_dir, exist_ok=True)
        if not os.path.isdir(output_dir):
            print("Can not create the output directory: '{}'".format(output_dir), file=sys.stderr)
            return 1

    #   加载 xml 格式的 schema 文件
    svc, error = load_schema_xml(param_file)
    if len(error) != 0:
        print(error, file=sys.stderr)
        return 1

    #   加载 json 格式的 schema 文件
    json_string, error = gen_schema_json(svc)
    if len(error) != 0:
        print(error, file=sys.stderr)
        return 1

    #   输出到文件
    json_file = os.path.join(output_dir, os.path.basename(param_file) + '.json')
    with open(json_file, "w") as file:
        print(json_string, file=file)

    #   写 清单 文件，如果清单文件未指定，那么不生成清单文件
    if list_file != "":
        with open(list_file, "w") as file:
            print(json_file, file=file)

    return 0


def cmd_help(argv: [str]) -> int:
    print("Usage:")
    print("     rcscc.py <PARAM-FILE> [-o <OUTPUT-DIR>] [-l <OUTPUT-LIST-FILE>]")
    return 0


def main(argv: [str]) -> int:
    if len(argv) >= 2:
        if argv[1] == '-h' or argv[1] == '--help':
            return cmd_help(argv)

    return cmd_compile(argv)


if __name__ == '__main__':
    sys.exit(main(sys.argv))
